package
{
	import flash.display.GradientType;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.geom.Matrix;
	import flash.geom.Point;

	[SWF(backgroundColor=0xffffff, width=465, height=465, frameRate=60)]
	public class path_test extends Sprite
	{

		public function path_test()
		{
			var path:Path = new Path();
			var s:Shape = new Shape();
			addChild(s);

			//イラレで作ったSVGから抜粋
			var data:XML = <data>
					<path d="M16.781,41.216L4.71,55.643L0,50.932l7.948-8.831c3.925-4.515,6.868-8.538,8.833-12.07v-7.36L2.943,26.202l-1.765-6.184l15.604-2.942V0h5.298c2.158,0.395,2.355,1.181,0.589,2.355v12.071l4.416,2.649c1.96,0.982,1.667,2.063-0.883,3.238l-3.533,5.005v3.533c8.636-6.869,16.879-10.304,24.729-10.304c5.886,0,10.401,1.57,13.542,4.71c3.728,3.73,5.593,8.539,5.593,14.425c0,6.085-1.668,10.795-5.003,14.131c-4.32,4.32-10.699,8.049-19.137,11.188l-2.945-5.888c7.458-2.748,12.856-5.791,16.193-9.127c2.746-2.748,4.121-6.183,4.121-10.305c0-4.121-1.375-7.554-4.121-10.305c-2.16-2.157-5.105-3.237-8.833-3.237c-7.065,0-15.114,4.123-24.14,12.363v30.913h-5.888V41.216z"/>
					<path d="M102.435,37.209c-4.908,0-9.128,2.945-12.659,8.831c-2.16,3.534-4.32,8.048-6.478,13.543l-6.771-3.532c8.046-12.562,17.664-30.421,28.85-53.582l6.478,3.532c1.373,1.375,1.078,2.16-0.883,2.355c-4.123,6.086-9.03,14.429-14.721,25.024c3.335-1.373,6.28-2.06,8.833-2.06c2.355,0,4.218,0.687,5.593,2.06c1.373,1.375,2.255,4.123,2.65,8.243l1.766,11.188c0.393,2.55,1.962,3.826,4.71,3.826c2.551,0,4.711-0.883,6.478-2.649c3.729-3.729,6.671-11.089,8.831-22.081l6.478,3.241c-2.552,11.186-5.595,18.546-9.127,22.078c-3.926,3.926-8.341,5.889-13.247,5.889c-6.085,0-9.618-3.237-10.598-9.716l-2.063-12.659C106.163,38.386,104.791,37.209,102.435,37.209z"/>
					<path d="M188.227,20.019c-5.103,0.982-9.911,1.769-14.426,2.355c-2.945,17.469-6.575,31.306-10.891,41.512l-6.478-2.062c5.298-12.363,9.028-25.316,11.188-38.86c-4.908,0.393-9.128,0.589-12.661,0.589l-1.178-5.89c5.496,0,10.401-0.194,14.721-0.587c0.785-6.083,1.471-11.383,2.06-15.899l5.888,1.179c2.158,0.59,2.355,1.375,0.59,2.355c-0.59,2.355-1.375,6.28-2.355,11.775c4.513-0.588,8.634-1.373,12.364-2.355L188.227,20.019z M214.429,56.819v6.479h-12.953c-7.46,0-14.526-1.768-21.197-5.3l2.65-5.299c5.101,2.747,11.284,4.12,18.547,4.12H214.429z M212.957,32.385c-11.383,0.394-20.314,1.963-26.79,4.711l-1.768-5.889c8.44-3.335,17.566-5.005,27.379-5.005L212.957,32.385z M200.003,5.888c3.532,2.161,7.36,4.713,11.481,7.656l-2.648,4.119c-4.32-2.942-8.148-5.492-11.483-7.652L200.003,5.888z M205.891,0c3.14,1.768,6.965,4.32,11.481,7.655l-2.648,4.12c-4.32-3.14-8.147-5.689-11.482-7.652L205.891,0z"/>
					<path d="M248.763,44.258c-7.459,6.086-13.25,11.287-17.371,15.605l-5.002-5.007c5.297-5.102,10.891-10.108,16.777-15.014L248.763,44.258z M258.478,58.39c2.354,0,4.021-0.49,5.004-1.473c1.178-1.175,1.768-3.237,1.768-6.183c0-3.141-0.885-5.593-2.649-7.359c-2.355-2.354-5.005-4.219-7.948-5.594c-4.711-2.159-7.559-3.729-8.538-4.711c-0.982-0.98-1.473-2.354-1.473-4.12c0-1.766,0.882-4.318,2.65-7.655l7.36,2.944c-1.57,2.161-2.355,3.63-2.355,4.416c0,0.589,1.078,1.375,3.238,2.354c6.083,2.751,10.203,5.203,12.363,7.36c3.141,3.141,4.711,7.264,4.711,12.364c0,5.104-1.275,8.933-3.826,11.482c-2.355,2.355-5.791,3.534-10.305,3.534c-3.926,0-8.441-1.278-13.542-3.828l2.06-7.066C251.901,57.212,255.729,58.39,258.478,58.39z M246.408,9.519l3.532-5.889c6.281,3.338,12.854,5.988,19.725,7.951l-3.238,6.476C257.007,14.721,250.333,11.873,246.408,9.519z M289.685,55.444c-5.496-8.438-10.404-14.719-14.721-18.842l5.3-3.824c6.28,5.888,11.283,12.07,15.013,18.546L289.685,55.444z"/>
					<path d="M353.046,55.331c2.354-2.355,3.531-5.495,3.531-9.42c0-4.316-1.276-7.751-3.826-10.305c-2.551-2.55-6.574-3.827-12.069-3.827c-6.478,0-11.483,1.277-15.017,3.827c-3.335,2.355-7.652,6.576-12.953,12.659l-5.003-4.416c14.131-14.131,24.827-25.906,32.087-35.328c-6.475,0.786-12.953,1.474-19.429,2.062l-2.06-6.771c9.615,0,18.151-0.49,25.611-1.473l2.65-1.179l3.238,3.24c2.157,1.963,1.668,3.238-1.473,3.826c-5.889,6.676-11.775,13.25-17.664,19.727c4.12-0.98,7.556-1.473,10.303-1.473c7.459,0,12.954,1.767,16.486,5.298c3.533,3.535,5.301,8.048,5.301,13.543c0,5.693-1.57,10.108-4.71,13.249c-4.711,4.71-10.698,7.065-17.96,7.065c-6.869,0-11.873-1.57-15.013-4.71c-2.553-2.551-3.828-5.594-3.828-9.128c0-2.355,0.882-4.415,2.65-6.182c1.96-1.962,4.218-2.944,6.771-2.944c3.533,0,6.479,1.177,8.833,3.532c2.745,2.75,4.61,7.068,5.593,12.954C348.237,58.769,350.886,57.492,353.046,55.331z M335.677,49.737c-1.375-1.372-2.748-2.06-4.123-2.06c-1.57,0-2.748,0.393-3.533,1.178c-0.98,0.982-1.471,2.063-1.471,3.237c0,1.769,0.785,3.436,2.355,5.006c1.961,1.963,4.514,2.943,7.654,2.943c0.784,0,1.667,0,2.649,0C338.619,54.94,337.442,51.506,335.677,49.737z"/>
				</data>;

			var x:Number = 0, y:Number = 0;
			for each (var p:XML in data.path)
			{
				var tokens:String = p.@d;
				for each (var token:String in tokens.match(/([cChHlLMVv][^a-z]+)|z/ig))
				{
					var command:String = token.charAt(0);
					var args:Array = token.substring(1).match(/[-0-9.][0-9.]*/g);
					args.every(function():Boolean
						{
							args[arguments[1]] = Number(arguments[0]);
							return true;
						});

					switch (command)
					{
						case "c":
							path.curveTo(x + args[0], y + args[1], x + args[2], y + args[3], x + args[4], y + args[5]);
							x += args[4];
							y += args[5];
							break;
						case "C":
							path.curveTo(args[0], args[1], args[2], args[3], args[4], args[5]);
							x = args[4];
							y = args[5];
							break;
						case "l":
							path.lineTo(x + args[0], y + args[1]);
							x += args[0];
							y += args[1];
							break;
						case "L":
							path.lineTo(args[0], args[1]);
							x = args[0];
							y = args[1];
							break;
						case "v":
							path.lineTo(x, y + args[0]);
							y += args[0];
							break;
						case "V":
							path.lineTo(x, args[0]);
							y = args[0];
							break;
						case "h":
							path.lineTo(x + args[0], y);
							x += args[0];
							break;
						case "H":
							path.lineTo(args[0], y);
							x = args[0];
							break;
						case "M":
							path.moveTo(args[0], args[1]);
							x = args[0];
							y = args[1];
							break;
						case "z":
							path.close();
							break;
					}
				}
			}

			path.applyMatrix(new Matrix(3, 0, 0, 3));
			path.debug(s.graphics, 150);

			var s2:Shape = new Shape();
			var m:Matrix = new Matrix();
			m.createGradientBox(20, 20, Math.PI / 2, -10, -10);
			s2.graphics.beginGradientFill(GradientType.LINEAR, [0, 0xffffff], [1, 1], [0, 255], m);
			s2.graphics.drawRect(-10, -10, 20, 20);
			addChild(s2);

			var t:int = 0;
			var point1:Point = new Point();
			var point2:Point = new Point();
			var n:uint = 0;
			var len:int = path.getLength();
			addEventListener(Event.ENTER_FRAME, function():void
				{
					path.pointForT(t / len, point1);
					var r:Number = 0;
					if (t > 0)
					{
						path.pointForT((t - 1) / len, point2);
						r = Math.atan2(point1.x - point2.x, point1.y - point2.y);
					}

					s2.x = point1.x;
					s2.y = point1.y;
					s2.rotation = 180 * -r / Math.PI + 90;

					if (++t > len)
					{
						t = 0;
					}
				});
		}
	}
}

//以下自作ライブラリから抜粋
//http://sweezy.googlecode.com/svn/trunk/sweezy-anim/src/sweezy/anim/util/Path.as
import flash.display.Graphics;
import flash.geom.Matrix;
import flash.geom.Point;

class Path
{

	private var _lastX:Number;

	private var _lastY:Number;

	private var _open:Boolean;

	private var _pathInfos:Array;

	private var _totalLength:Number;

	private var _x:Number;

	private var _y:Number;

	public function Path()
	{
		_x = 0;
		_y = 0;
		_lastX = 0;
		_lastY = 0;
		_pathInfos = [];
		_open = false;
	}

	public function add(path:Path):void
	{
		if (path === null)
		{
			return;
		}

		_pathInfos = _pathInfos.concat(path._pathInfos);
		_totalLength = NaN;
		_x = path._x;
		_y = path._y;
		_lastX = path._lastX;
		_lastY = path._lastY;
		_open = path._open;
	}

	public function applyMatrix(matrix:Matrix):void
	{
		if (matrix === null)
		{
			return;
		}

		var point:Point = new Point();
		var info:PathInfo;

		var a:Number = matrix.a;
		var b:Number = matrix.b;
		var c:Number = matrix.c;
		var d:Number = matrix.d;
		var tx:Number = matrix.tx;
		var ty:Number = matrix.ty;
		var x:Number, y:Number;

		x = _x;
		y = _y;
		_x = x * a + y * c + tx;
		_y = x * b + y * d + ty;
		x = _lastX;
		y = _lastY;
		_lastX = x * a + y * c + tx;
		_lastY = x * b + y * d + ty;

		for (var i:int = 0, len:uint = _pathInfos.length; i < len; i++)
		{
			info = _pathInfos[i] as PathInfo;

			x = info.sx;
			y = info.sy;
			info.sx = x * a + y * c + tx;
			info.sy = x * b + y * d + ty;
			x = info.ex;
			y = info.ey;
			info.ex = x * a + y * c + tx;
			info.ey = x * b + y * d + ty;

			if (info.type !== 0)
			{
				x = info.cx1;
				y = info.cy1;
				info.cx1 = x * a + y * c + tx;
				info.cy1 = x * b + y * d + ty;
				x = info.cx2;
				y = info.cy2;
				info.cx2 = x * a + y * c + tx;
				info.cy2 = x * b + y * d + ty;
			}

			info.computeLength();
		}
	}

	public function close():void
	{
		if (!_open)
		{
			return;
		}
		if (_x === _lastX && _y === _lastY)
		{
			return;
		}

		lineTo(_lastX, _lastY);
	}

	public function curveTo(controlX1:Number, controlY1:Number, controlX2:Number, controlY2:Number, anchorX:Number, anchorY:Number):void
	{
		addPath(2, anchorX, anchorY, controlX1, controlY1, controlX2, controlY2);
	}

	public function debug(g:Graphics, div:int):void
	{
		g.clear();
		g.lineStyle(1);

		var info:PathInfo;
		var point:Point = new Point();

		for (var i:int = 0, len:uint = _pathInfos.length; i < len; i++)
		{
			info = _pathInfos[i] as PathInfo;
			g.moveTo(info.sx, info.sy);

			if (info.type === 0)
			{
				g.lineTo(info.ex, info.ey);
			}
			else
			{
				for (var j:int = 0; j <= 20; j++)
				{
					info.pointForT(j / 20, point);
					g.lineTo(point.x, point.y);
				}
			}
		}

		g.lineStyle();
		for (var k:int = 0; k <= div; k++)
		{
			pointForT(k / div, point);
			g.beginFill(0xff0000, 0.5);
			g.drawCircle(point.x, point.y, 2);
			g.endFill();
		}
	}

	public function getLength():Number
	{
		if (isNaN(_totalLength))
		{
			_totalLength = 0;
			for (var i:int = 0, len:uint = _pathInfos.length; i < len; i++)
			{
				_totalLength += (_pathInfos[i] as PathInfo).len;
			}
		}

		return _totalLength;
	}

	public function lineTo(x:Number, y:Number):void
	{
		if (_x === x && _y === y)
		{
			return;
		}

		addPath(0, x, y, NaN, NaN, NaN, NaN);
	}

	public function moveTo(x:Number, y:Number):void
	{
		if (_x === x && _y === y)
		{
			return;
		}

		_x = x;
		_y = y;
		_lastX = x;
		_lastY = y;
	}

	public function pointForT(t:Number, point:Point):void
	{
		if (isNaN(t) || t < 0 || t > 1 || point === null)
		{
			return;
		}

		var target:Number = getLength() * t;
		var start:Number = 0;
		var end:Number = 0;
		var info:PathInfo;
		for (var i:int = 0, len:uint = _pathInfos.length; i < len; i++)
		{
			info = _pathInfos[i] as PathInfo;
			start = end;
			end += info.len;

			if (start <= target && target <= end)
			{
				break;
			}
		}

		info.pointForT(1 - (end - target) / info.len, point);
	}

	public function quadTo(controlX:Number, controlY:Number, anchorX:Number, anchorY:Number):void
	{
		addPath(1, anchorX, anchorY, controlX, controlY, NaN, NaN);
	}

	private function addPath(type:uint, endX:Number, endY:Number, cx1:Number, cy1:Number, cx2:Number, cy2:Number):void
	{
		var info:PathInfo = new PathInfo();
		info.type = type;
		info.sx = _x;
		info.sy = _y;
		info.ex = endX;
		info.ey = endY;

		if (type === 1)
		{
			var d:Number = 2 / 3;
			info.cx1 = _x + (cx1 - _x) * d;
			info.cy1 = _y + (cy1 - _y) * d;
			info.cx2 = endX + (cx1 - endX) * d;
			info.cy2 = endY + (cy1 - endY) * d;
		}
		else
		{
			info.cx1 = cx1;
			info.cy1 = cy1;
			info.cx2 = cx2;
			info.cy2 = cy2;
		}

		info.computeLength();
		_pathInfos[_pathInfos.length] = info;

		_x = endX;
		_y = endY;
		_open = !(_lastX === endX && _lastY === endY);
		_totalLength = NaN;
	}
}


class PathInfo
{

	private static const N:int = 50;

	public var cx1:Number;

	public var cx2:Number;

	public var cy1:Number;

	public var cy2:Number;

	public var ex:Number;

	public var ey:Number;

	public var len:Number;

	public var sx:Number;

	public var sy:Number;

	public var type:uint;

	public function computeLength():void
	{
		if (type === 0)
		{
			len = Math.sqrt((sx - ex) * (sx - ex) + (sy - ey) * (sy - ey));
		}
		else
		{
			len = getBezierLength(0, 1);
		}
	}

	public function pointForT(t:Number, point:Point):void
	{
		if (type === 0)
		{
			point.x = sx + (ex - sx) * t;
			point.y = sy + (ey - sy) * t;
		}
		else
		{
			t = getCoordFromLength(len * t);
			bezierPointForT(t, point);
		}
	}

	private function bezierPointForT(t:Number, point:Point):void
	{
		var x0:Number = cx1 - sx;
		var y0:Number = cy1 - sy;
		var x1:Number = cx2 - cx1 - x0;
		var y1:Number = cy2 - cy1 - y0;
		var x2:Number = ex - cx2 - x1 - cx2 + cx1;
		var y2:Number = ey - cy2 - y1 - cy2 + cy1;

		point.x = x2 * t * t * t + 3 * x1 * t * t + 3 * x0 * t + sx;
		point.y = y2 * t * t * t + 3 * y1 * t * t + 3 * y0 * t + sy;
	}

	private function getBezierLength(ta:Number, tb:Number):Number
	{
		var t:Number, x:Number, y:Number, ft:Number;
		var t2:Number = (tb - ta) / (2 * N);
		var x0:Number = cx1 - sx;
		var y0:Number = cy1 - sy;
		var x1:Number = cx2 - cx1 - x0;
		var y1:Number = cy2 - cy1 - y0;
		var x2:Number = ex - cx2 - x1 - cx2 + cx1;
		var y2:Number = ey - cy2 - y1 - cy2 + cy1;

		x = 3 * (x2 * ta * ta + 2 * x1 * ta + x0);
		y = 3 * (y2 * ta * ta + 2 * y1 * ta + y0);
		ft = Math.sqrt(x * x + y * y);

		x = 3 * (x2 * tb * tb + 2 * x1 * tb + x0);
		y = 3 * (y2 * tb * tb + 2 * y1 * tb + y0);
		ft += Math.sqrt(x * x + y * y);

		for (var i:int = 1; i <= N; i++)
		{
			t = ta + (2 * i - 1) * t2;
			x = 3 * (x2 * t * t + 2 * x1 * t + x0);
			y = 3 * (y2 * t * t + 2 * y1 * t + y0);
			ft += Math.sqrt(x * x + y * y) * 4;

			t = ta + 2 * i * t2;
			x = 3 * (x2 * t * t + 2 * x1 * t + x0);
			y = 3 * (y2 * t * t + 2 * y1 * t + y0);
			ft += i < N ? Math.sqrt(x * x + y * y) * 2 : 0;
		}

		return t2 / 3 * ft;
	}

	private function getCoordFromLength(len:Number):Number
	{
		var x:Number, y:Number, v:Number, v2:Number;
		var t:Number = 1, d:Number = 0;
		var x0:Number = cx1 - sx;
		var y0:Number = cy1 - sy;
		var x1:Number = cx2 - cx1 - x0;
		var y1:Number = cy2 - cy1 - y0;
		var x2:Number = ex - cx2 - x1 - cx2 + cx1;
		var y2:Number = ey - cy2 - y1 - cy2 + cy1;

		if (len === 0)
		{
			return 0;
		}
		else if (len >= this.len)
		{
			return 1;
		}

		t = len / this.len;
		while (true)
		{
			v2 = getBezierLength(0, t);
			v = ((v = v2 - len) < 0 ? -v : v) / len;
			if (v <= 0.0001)
			{
				break;
			}

			x = 3 * (x2 * t * t + 2 * x1 * t + x0);
			y = 3 * (y2 * t * t + 2 * y1 * t + y0);
			d += x * x + y * y;
			t += (len - v2) / Math.sqrt(d);
		}

		return t;
	}
}
